package decision_tree.metric;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import decision_tree.dto.Sample;
import decision_tree.question.Question;

public class EntropyManager {
	private static EntropyManager singleton = new BinaryEntropyManager();
	public static EntropyManager getInstance() {
		return singleton;
	}
	
	public double getEntropy(List<Sample> sampleList) {
		Map<String, Integer> map = new HashMap<String, Integer>();
		for(Sample sample : sampleList) {
			if(!map.containsKey(sample.getY())) {
				map.put(sample.getY(), 0);
			}
			map.put(sample.getY(), map.get(sample.getY()) + 1);
		}
		
		double result = 0;
		for(Entry<String, Integer> entry : map.entrySet()) {
			int count = entry.getValue();
			double wi;
			if(count == 0) {
				wi = 0;
			} else {
			
				double prob = count / (double) sampleList.size();
				double information = Math.log(1 / prob) / Math.log(2.0);
				wi = prob * information;
			}
			
			result += wi;
		}
		return result;
	}
		
	public double getEntropyFromProb(List<Double> probList) {
		double entropy = 0;
		for(double prob : probList) {
			double information = Math.log(1 / prob) / Math.log(2.0);
			entropy += information;
		}
		entropy /= probList.size();
		return entropy;
	}
	
	public double getConditionalEntropy(List<Sample> sampleList, Question q) {
		Map<Integer, List<Sample>> map = splitData(sampleList, q);
		
		double result = 0;
		for(Entry<Integer, List<Sample>> entry : map.entrySet()) {
			double coe = entry.getValue().size() / (double)sampleList.size();
			double entropy = getEntropy(entry.getValue());
			result += coe * entropy;
		}
		return result;
	}
	
	public Map<Integer, List<Sample>> splitData(List<Sample> sampleList, Question q) {
		Map<Integer, List<Sample>> map = new HashMap<Integer, List<Sample>>();
		for(Sample sample : sampleList) {
			int category = q.ask(sample);
			if(!map.containsKey(category)) {
				map.put(category, new LinkedList<Sample>());
			}
			map.get(category).add(sample);
		}
		return map;
	}
}
